#ifndef _HANDLE_HPP_
#define _HANDLE_HPP_

#include <Windows.h>

#include <cstdint>

namespace windows
{

namespace detail
{

template <int8_t InvalidValue>
class handle
{
public:
    explicit handle() noexcept = default;
    handle(const HANDLE raw_handle) noexcept;
    explicit handle(const handle& other) = delete;
    explicit handle(handle&& other) noexcept;
    ~handle();

    handle& operator=(const HANDLE other) noexcept;
    handle& operator=(const handle& other) = delete;
    handle& operator=(handle&& other) noexcept;
    operator HANDLE() const noexcept;

    bool is_valid() const noexcept;

private:
    HANDLE raw_handle = reinterpret_cast<decltype(raw_handle)>(InvalidValue);
};

template <int8_t InvalidValue>
handle<InvalidValue>::handle(const HANDLE raw_handle) noexcept : raw_handle(raw_handle)
{
}

template <int8_t InvalidValue>
handle<InvalidValue>::handle(handle&& other) noexcept : raw_handle(other.raw_handle)
{
    other.raw_handle = reinterpret_cast<decltype(raw_handle)>(InvalidValue);
}

template <int8_t InvalidValue>
handle<InvalidValue>::~handle()
{
    if (raw_handle != reinterpret_cast<decltype(raw_handle)>(InvalidValue))
        ::CloseHandle(raw_handle);
}

template <int8_t InvalidValue>
handle<InvalidValue>& handle<InvalidValue>::operator=(const HANDLE other) noexcept
{
    this->raw_handle = other;

    return *this;
}

template <int8_t InvalidValue>
handle<InvalidValue>& handle<InvalidValue>::operator=(handle&& other) noexcept
{
    raw_handle = other.raw_handle;
    other.raw_handle = reinterpret_cast<decltype(raw_handle)>(InvalidValue);
}

template <int8_t InvalidValue>
handle<InvalidValue>::operator HANDLE() const noexcept
{
    return raw_handle;
}

template <int8_t InvalidValue>
bool handle<InvalidValue>::is_valid() const noexcept
{
    return raw_handle != reinterpret_cast<decltype(raw_handle)>(InvalidValue);
}

} // namespace detail

using handle = detail::handle<0>;
using file_handle = detail::handle<-1>;

} // namespace windows

#endif
